//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.eResearch.RuntimeServices.Tracking
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Workflow.Runtime.Tracking;
    using Microsoft.Research.eResearch.Blackboard.Common;
    using Microsoft.Research.eResearch.Common.Linq;
    using Microsoft.Research.eResearch.Ontology.Lookup;
    using Microsoft.Research.eResearch.RuntimeServices.Blackboard;
    using Microsoft.Research.eResearch.RuntimeServices.Ontology;
    using System.Collections;
    using Microsoft.Research.eResearch.RuntimeServices.Common;

    public class PropertyTrackingChannel : TrackingChannel
    {
        private TrackingParameters trackingParameters = null;

        private IServiceProvider serviceProvider = null;
        const string notApplicable = "N/A - marked as Workflow Output";
        const string overSized = "N/A - data size ({0} kb) exceeded configured display size limit ({1} kb).";
        const int kiloBits = 1024;

        public PropertyTrackingChannel(IServiceProvider serviceProvider, TrackingParameters trackingParameters)
        {
            this.trackingParameters = trackingParameters;
            this.serviceProvider = serviceProvider;
        }

        protected override void InstanceCompletedOrTerminated()
        {
        }

        protected override void Send(TrackingRecord record)
        {
            // Retrieve the OutputParams HashTable from the JobExecutionService
            OntologyMappingService mapper = serviceProvider.GetOntologyMappingService();
            IBlackboardService blackboard = serviceProvider.GetBlackboardService();
            JobExecutionService jobIdService = serviceProvider.GetJobExecutionService();

            // Perform the mapping of the tracking record
            List<List<ConceptValuePair>> recordPairGroups = mapper.PerformMapping(record);

            // In the case of workflow tracking record, add the workflow name concept
            // This logic is not added in WorkflowTrackingRecordMapper because of reverse dependency.
            // Concept value is retrieved from runtime service not from TrackingRecord
            if (record as WorkflowTrackingRecord != null)
            {
                recordPairGroups.ForEach(pairGroup =>
                {
                    pairGroup.Add(new ConceptValuePair("trident:WorkflowName", jobIdService.WorkflowName));
                });
            }

            // Perform the mapping on the tracking parameters
            List<ConceptValuePair> parameterPairGroup = mapper.PerformMapping(trackingParameters).FirstOrDefault();

            // Figure out which concepts have been requested by subscribers
            List<string> subscribedConcepts =
                blackboard.LastSubscriptionProfile
                .Select(subscriptionItem => subscriptionItem.Name)
                .ToList();

            recordPairGroups
                // Add the pairs from the TrackingParameters to the end of each message
                .ForEachPassThrough(pairGroup =>
                {
                    if (parameterPairGroup != null)
                    {
                        pairGroup.AddRange(parameterPairGroup);
                    }

                    // Add the trident:JobId concept
                    pairGroup.Add(jobIdService.JobIdConcept);
                })
                // Filter out only those concepts that have subscribers
                .Select(pairGroup => pairGroup.Where(pair => subscribedConcepts.Contains(pair.Concept)))
                // Map each List<ConceptValuePair> into a BlackboardMessage
                .Select(pairGroup =>
                {
                    BlackboardMessage message = new BlackboardMessage();
                    message.Add("trident:MachineName", System.Environment.MachineName);
                    if (record is ActivityTrackingRecord)
                    {
                        ActivityTrackingRecord rec = record as ActivityTrackingRecord;
                        message.Add("trident:ActivityType", rec.ActivityType.ToString());
                    }
                    pairGroup.ForEach(pair => message.Add(pair.Concept, pair.Value));
                    return message;
                })
                // Filter out the blank messages
                .Where(message => (message.Count) > 0)
                // And send each message to the blackboard
                .ForEach(message =>
                    {
                        #region Remove value filed is specified as worklfow output.
                        if (message.ContainsKey("trident:ActivityName") && message.ContainsKey("trident:FieldName"))
                        {
                            string paramKey = WorkflowRuntimeHelper.FetchActivityParamUniqueKey(message["trident:ActivityName"], message["trident:FieldName"]);
                            if (message.ContainsKey("trident:ActivityPropertyValue"))
                            {
                                if (jobIdService.OutputParamsHash != null && jobIdService.OutputParamsHash.ContainsKey(paramKey))
                                {
                                    message["trident:ActivityPropertyValue"] = notApplicable;
                                }
                                //Moving the messagesize check to else block, to avoid message size calculation overhead in case of workflow output. 
                                //Message size is calculated only if the property is not marked as workflow output
                                else
                                {
                                    long messageSize = message.GetMessageSize();
                                    if ((messageSize >= (BlackBoardSettings.SerializationSettings.MessageSize * kiloBits)))
                                    {
                                        message["trident:ActivityPropertyValue"] = string.Format(overSized, messageSize / kiloBits, BlackBoardSettings.SerializationSettings.MessageSize);
                                    }
                                }
                            }
                        }
                        #endregion

                        blackboard.PublishMessage(message);
                    });
        }
    }
}
